<?php

/**
 * Framework_Module
 *
 * @author      Joe Stump <joe@joestump.net>
 * @copyright   (c) 2006 Joseph C. Stump. All rights reserved.
 * @license     http://www.opensource.org/licenses/bsd-license.php
 * @package Framework
 * @filesource
 */

define('FRAMEWORK_MODULE_ERROR_INVALID_MODULE_FILE',        2);
define('FRAMEWORK_MODULE_ERROR_INVALID_MODULE_CLASS',       4);
define('FRAMEWORK_MODULE_ERROR_INVALID_EVENT',              8);
define('FRAMEWORK_MODULE_ERROR_INVALID_CONTROLLER',        16);

/**
 * Framework_Module
 *
 * The base module class. All applications will extends from this class. This
 * means each module will, by default, have an open DB connection and an
 * open log file to write to. Also, it's a good place to put functions,
 * variables, etc. that all modules need.
 *
 * @author Joe Stump <joe@joestump.net>
 * @package Framework
 */
abstract class Framework_Module extends Framework_Object_Web
{
	/**
	 * $presenter
	 *
	 * Used in Framework_Presenter::factory() to determine which presentation
	 * (view) class should be used for the module.
	 *
	 * @access      public
	 * @var         string      $presenter
	 * @see         Framework_Presenter, Framework_Presenter_common
	 * @see         Framework_Presenter_Smarty
	 */
	public $presenter = 'Smarty';

	/**
	 * $contollers
	 *
	 * This is an array of acceptable controllers. Not all modules will want
	 * to enable all controllers.
	 *
	 * @author      Joe Stump <joe@joestump.net>
	 * @access      public
	 * @var         array       $controllers     Array of acceptable controllers
	 * @see         Framework_Controller
	 */
	public $controllers = array('Web', 'REST');

	/**
	 * $data
	 *
	 * Data set by the module that will eventually be passed to the view.
	 *
	 * @author 	    Joe Stump <joe@joestump.net>
	 * @var 	    mixed 		$data 		Module data
	 * @access  	protected
	 * @see 		Framework_Module::set(), Framework_Module::getData()
	 */
	protected $data = array();

	/**
	 * $name
	 *
	 * @author 		Joe Stump <joe@joestump.net>
	 * @access 		public
	 * @var 		string 		$name 		Name of module class
	 */
	public $className = '';

	/**
	 * $tplFile
	 *
	 * @author 		Joe Stump <joe@joestump.net>
	 * @access  	public
	 * @var 		string 		$tplFile Name of template file
	 * @see 		Framework_Presenter_Smarty
	 */
	public $tplFile = '';

	/**
	 * $moduleName
	 *
	 * @author 		Joe Stump <joe@joestump.net>
	 * @access  	public
	 * @var 		string 		$moduleName 	Name of requested module
	 * @see 		Framework_Presenter_Smarty
	 */
	public $moduleName = null;

	/**
	 * $pageTemplateFile
	 *
	 * @access  	public
	 * @author 		Joe Stump <joe@joestump.net>
	 * @var 		string		$pageTemplateFile 		Name of outer page template
	 */
	public $pageTemplateFile = null;

	/**
	 * $templateCache
	 *
	 * @access  	public
	 * @var 		mixed		$templateCache		Array of cache information for use by
	 */
	public $templateCache = null;

	/**
	 * $authLayer
	 *
	 * @access  public
	 * @var 	string 		$authLayer 		The class which will be used to authenticate the user
	 * @see 	Framework_Auth_Common
	 */
	public $authLayer = 'No';

	/**
	 * $validEvents
	 *
	 * @access  public
	 * @var 	array 		$validEvents	Allowed events for the module, eg. http://site.com/module/class/event
	 */
	public $validEvents = array('__default');

	/**
	 * $forms
	 *
	 * @access  public
	 * @var 	array 		$forms	Array of PEAR::Quickform objects
	 */
	public $forms = array();


	/**
	 * $ran 
	 *
	 * @access  public
	 * @var 	boolean 	$ran	Whether or not the module ran
	 */
	public $ran = false;

	/**
	 * __construct
	 *
	 * @author 		Joe Stump <joe@joestump.net>
	 * @access 		public
	 * @return 		void
	 */
	public function __construct()
	{
		parent::__construct();

		$parts = explode('_',$this->me->getName());
		$this->className = array_pop($parts);
		$this->tplFile = $this->className.'.tpl';
	}

	/**
	 * __default
	 *
	 * This function is ran by the controller if an event is not specified
	 * in the user's request.
	 *
	 * @author 		Joe Stump <joe@joestump.net>
	 * @access 		public
	 * @return 		mixed
	 */
	abstract public function __default();

	/**
	 * __shutdown
	 *
	 * Used to tear down a Framework_Module class. This merely disconnects from
	 * the database and closes the log file. This should be ran from any child
	 * classes that use this function as well.
	 *
	 * @access 		public
	 * @return 		boolean
	 */
	public function __shutdown()
	{
		
		return true;
	}

	/**
	 * setData
	 *
	 * Set data for your module. This will eventually be passed toe the
	 * presenter class via Framework_Module::getData().
	 *
	 * @access 		public
	 * @param 		string 		$var	 Name of variable
	 * @param 		mixed 		$val 	 Value of variable
	 * @return 		void
	 * @see 		Framework_Module::getData()
	 */
	public function setData($var,$val) {
		$this->data[$var] = $val;
	}

	/**
	 * getData
	 *
	 * Returns module's data.
	 *
	 * @author 		Joe Stump <joe@joestump.net>
	 * @return 		mixed
	 * @see 		Framework_Presenter_common
	 */
	public function getData()
	{
		return $this->data;
	}

	/**
	 * __set
	 *
	 * @access public
	 * @return void
	 * @see Framework_Module::setData()
	 */
	public function __set($var,$val)
	{
		$this->setData($var,$val);
	}

	/**
	 * __get
	 *
	 * @access 		public
	 * @return 		mixed
	 * @see 		Framework_Module::$data
	 */
	public function __get($var)
	{
		if (!isset($this->data[$var])) {
			$this->data[$var] = null;
		}

		return $this->data[$var];
	}

	/**
	 * isValid
	 *
	 * Determines if $module is a valid framework module. This is used by
	 * the controller to determine if the module fits into our framework's
	 * mold. If it extends from bFramework_Module then
	 * it should be good to run.
	 *
	 * @author 		Joe Stump <joe@joestump.net>
	 * @static
	 * @param 		mixed 	module
	 * @return 		bool
	 */
	public static function isValid($module)
	{
		return (is_object($module) &&
		$module instanceof Framework_Module);
	}

	/**
	 * factory
	 *
	 * @access 		public
	 * @param 		object 		$request load
	 * @return 		mixed 		Framework module on success, PEAR_Error on failure
	 * @static
	 */
	public static function factory(Framework_Request_Common $request)
	{

		$file = 'Framework/Module/'.$request->module;
		$object = 'Framework_Module_'.$request->module;
		if (strlen($request->class)) {
			$file   .= '/'.$request->class.'.php';
			$object .= '_'.$request->class;
		} else {
			$file   .= '.php';
		}

		if (!include_once($file)) {
			return PEAR::raiseError('Module file not found: '.$file, FRAMEWORK_MODULE_ERROR_INVALID_MODULE_FILE);
		}

		if (!class_exists($object)) {
			return PEAR::raiseError('Module class not found: '.$request->class, FRAMEWORK_MODULE_ERROR_INVALID_MODULE_CLASS);
		}

		try {
			$instance = new $object();
			if (!Framework_Module::isValid($instance)) {
				return PEAR::raiseError('Invalid module class loaded');
			}
		} catch (Framework_Exception $error) {
			return PEAR::raiseError($error->getMessage());
		}
		if (!$instance->moduleName) {
			$instance->moduleName = $request->module;
		}
		
		return $instance;
	}

	/**
	 * start
	 *
	 * Accepts the module and the request in which the module should be ran
	 * and runs the appropriate event. Additionally, if the request specifies
	 * a different presenter and the presenter is listed in the list of
	 * acceptable presenters then it switches the presenter to be used.
	 *
	 * @access      public
	 * @param       object      $module         Module to load
	 * @param       object      $request        Request to load
	 * @return      mixed       Framework module on success,
	 *                          PEAR_Error on failure
	 * @static
	 */
	static public function start(Framework_Module $module, $request)
	{

		$event = $request->getEvent($module);
		 
		if (PEAR::isError($event) || !in_array($event,$module->validEvents)) {
			return $event;
		}

		try {
			$result = $module->{$event}();
			if (PEAR::isError($result)) {
				return $result;
			}
		} catch (Framework_Exception $error) {
			return PEAR::raiseError($error->getMessage());
		}

		if (!is_null($request->presenter) &&
		$request->presenter != $module->presenter) {
			if (is_array($module->presenter) &&
			in_array($request->presenter, $module->presenter)) {
				$module->presenter = $request->presenter;
			}
		}

		return true;
	}

	/**
	 * stop
	 *
	 * @access public
	 * @param object $module
	 * @return mixed True on success, PEAR_Error on failure
	 * @static
	 */
	static public function stop($module)
	{
		if (method_exists($module,'__shutdown')) {
			try {
				$result = $module->__shutdown();
				if (PEAR::isError($result)) {
					return $result;
				}
			} catch (Framework_Exception $error) {
				return PEAR::raiseError($error->getMessage());
			}
		}
		return true;
	}

}

?>
